package com.yanlight.util;

import com.alibaba.fastjson.JSONObject;
import com.yanlight.app.model.wx.Certificate;
import com.yanlight.app.model.wx.CertificateData;
import com.yanlight.app.request.MicroApplyStatusRequest;
import com.yanlight.app.request.WxCertRequest;
import com.yanlight.app.response.ApplyStatusResponse;
import com.yanlight.app.response.WxCertResponse;
import com.yanlight.support.WxConstants;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.util.*;

public class WxRequestUtil {

    private static final String ALGORITHM = "AES/GCM/NoPadding";

    private static final int TAG_LENGTH_BIT = 128;

    /**
     * 真实上传方法
     *
     * @param file
     * @param mch_id
     * @param requestUrl
     * @param payKey
     */
    @SuppressWarnings("deprecation")
    public static String upload(File file, String mch_id, String requestUrl, String payKey) {

        String mediaId = null;
        try {
            String media_hash = MD5Util.md5HashCode32(new FileInputStream(file));
            String sign_type = "HMAC-SHA256";
            SortedMap<String, String> paramMap = new TreeMap<>();
            paramMap.put("mch_id", mch_id);
            paramMap.put("media_hash", media_hash.toLowerCase());
            paramMap.put("sign_type", sign_type);
            String sign = SignatureUtil.createApiSignHMAC(paramMap, payKey);

            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
            multipartEntityBuilder.addTextBody("mch_id", mch_id, ContentType.MULTIPART_FORM_DATA);
            multipartEntityBuilder.addBinaryBody("media", file, ContentType.create("image/jpg"), file.getName());
            multipartEntityBuilder.addTextBody("media_hash", media_hash, ContentType.MULTIPART_FORM_DATA);
            multipartEntityBuilder.addTextBody("sign_type", sign_type, ContentType.MULTIPART_FORM_DATA);
            multipartEntityBuilder.addTextBody("sign", sign, ContentType.MULTIPART_FORM_DATA);

            SSLContext sslcontext = getSSLContent(mch_id);
            if (sslcontext == null) {
                LogUtil.showLog("官方微信--证书加载失败");
                return null;
            }

            SSLConnectionSocketFactory sslConn = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslConn).build();
            try {
                HttpPost httpPost = new HttpPost(requestUrl);
                RequestConfig config = RequestConfig.custom().setConnectTimeout(10000).setConnectionRequestTimeout(10000).setSocketTimeout(10000).build();
                httpPost.setConfig(config);
                ////这里的Content-type要设置为"multipart/form-data"，否则返回“参数填写有误，请检查后重试”
                httpPost.addHeader(HTTP.CONTENT_TYPE, "multipart/form-data; charset=UTF-8");
//                httpPost.addHeader(HTTP.USER_AGENT, "wxpay sdk java v1.0 " + mch_id);
                httpPost.setEntity(multipartEntityBuilder.build());
                CloseableHttpResponse response = httpclient.execute(httpPost);
                String result = EntityUtils.toString(response.getEntity(), "UTF-8");

                Map<String, String> stringStringMap = XmlUtil.xmlToMap(result);
                String media_id = stringStringMap.get("media_id");
                mediaId = media_id;

            } catch (Exception e) {
                LogUtil.showLog("官方微信--请求失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mediaId;
    }

    public static SSLContext getSSLContent(String mch_id) {
        FileInputStream instream = null;
        SSLContext sslcontext = null;
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            instream = new FileInputStream(WxConstants.certPath);
            keyStore.load(instream, mch_id.toCharArray());
            sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mch_id.toCharArray()).build();
        } catch (Exception e) {

        } finally {
            try {
                if (instream != null) {
                    instream.close();
                }
            } catch (IOException e) {

            }
        }
        return sslcontext;
    }

    public static ApplyStatusResponse queryApplyStatus(String businessId, String applymentId, String mchKey) {

        MicroApplyStatusRequest request = new MicroApplyStatusRequest();
        request.setNonce_str(RandomStringUtil.randomAlphanumeric(32));
        request.setBusiness_code(businessId);
        request.setApplyment_id(applymentId);
        request.setMch_id(WxConstants.mchId);

        SortedMap<String, String> stringStringSortedMap = request.setupParams();
        String apiSign = SignatureUtil.createApiSignHMAC(stringStringSortedMap, mchKey);
        request.setSign(apiSign);

        String xmlFromObj = XmlParseUtil.getXmlFromObj(request);
        String result = WxHttpClientUtil.xmlPost(WxConstants.microStatusUrl, WxConstants.mchId, WxConstants.certPath, xmlFromObj);
        Map<String, String> stringStringMap = XmlUtil.xmlToMap(result);

        try {
            ApplyStatusResponse bean = Map2BeanUtil.mapToObject(stringStringMap, ApplyStatusResponse.class);
            return bean;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public static String getCertificateNO(String mchId, String mchKey) {
        String serial_no = "";

        WxCertRequest certSign = new WxCertRequest();
        String nonceStr = RandomStringUtil.randomAlphanumeric(32);

        certSign.setNonce_str(nonceStr);
        certSign.setMch_id(mchId);
        SortedMap<String, String> stringStringSortedMap = certSign.signParams();
        String apiSign = SignatureUtil.createApiSignHMAC(stringStringSortedMap, mchKey);

        certSign.setSign(apiSign);
        WxCertResponse execute = certSign.execute();
        String certificates = execute.getCertificates();

        CertificateData certificateData = JSONObject.parseObject(certificates, CertificateData.class);

        if (certificateData != null && certificateData.getData() != null && certificateData.getData().size() > 0) {
            List<Certificate> data = certificateData.getData();
            Certificate certificate = data.get(0);

            serial_no = certificate.getSerial_no();

            final String associatedData = certificate.getEncrypt_certificate().getAssociated_data(); // encrypt_certificate.associated_data
            final String nonce = certificate.getEncrypt_certificate().getNonce(); // encrypt_certificate.nonce
            final String cipherText = certificate.getEncrypt_certificate().getCiphertext(); // encrypt_certificate.ciphertext

            try {
                String certificateValue = aesgcmDecrypt(associatedData, nonce, cipherText);
                if (certificateValue != null) {
                    WxEncryptUtil.initCert(certificateValue);
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
        return serial_no;
    }


    private static String aesgcmDecrypt(String aad, String nonce, String cipherText) {
        try {
            final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
            SecretKeySpec key = new SecretKeySpec(WxConstants.api3.getBytes(), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(aad.getBytes());
            return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
        } catch (Exception e) {
            return null;
        }
    }

}
